package com.xgk.boot.module.core.service.customer;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mzt.logapi.context.LogRecordContext;
import com.mzt.logapi.starter.annotation.LogRecord;
import com.xgk.boot.framework.common.constant.ErrorCodeConstants;
import com.xgk.boot.framework.security.core.util.SecurityFrameworkUtils;
import com.xgk.boot.module.core.enums.biz.CurrencyEnum;
import com.xgk.boot.framework.common.exception.util.ServiceExceptionUtil;
import com.xgk.boot.framework.common.pojo.PageResult;
import com.xgk.boot.framework.common.util.object.BeanUtils;
import com.xgk.boot.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.xgk.boot.framework.tenant.core.aop.TenantIgnore;
import com.xgk.boot.module.core.controller.admin.auth.vo.AdminUserVO;
import com.xgk.boot.module.core.controller.admin.customer.vo.CustomerListVO;
import com.xgk.boot.module.core.controller.admin.customer.vo.CustomerReqVO;
import com.xgk.boot.module.core.controller.admin.customer.vo.CustomerSaveVO;
import com.xgk.boot.module.core.controller.admin.customer.vo.CustomerVO;
import com.xgk.boot.module.core.controller.admin.user.vo.user.UserSaveReqVO;
import com.xgk.boot.module.core.dal.dto.PaymentSummaryDto;
import com.xgk.boot.module.core.dal.entity.customer.CustomerBalanceDO;
import com.xgk.boot.module.core.dal.entity.customer.CustomerDO;
import com.xgk.boot.module.core.dal.entity.user.AdminUserDO;
import com.xgk.boot.module.core.dal.mapper.customer.CustomerMapper;
import com.xgk.boot.module.core.dal.iservice.CustomerService;
import com.xgk.boot.module.core.dal.redis.RedisKeyConstants;
import com.xgk.boot.module.core.enums.permission.RoleCodeEnum;
import com.xgk.boot.module.core.service.payment.PaymentRecordsServiceImpl;
import com.xgk.boot.module.core.service.permission.UserRoleServiceImpl;
import com.xgk.boot.module.core.service.user.AdminUserServiceImpl;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.xgk.boot.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.xgk.boot.module.core.enums.LogRecordConstants.*;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author yfbao
 * @since 2025-06-14
 */
@Slf4j
@Service
public class CustomerServiceImpl extends ServiceImpl<CustomerMapper, CustomerDO> implements CustomerService {

    @Lazy
    @Autowired
    private CustomerServiceImpl self; // 注入自身代理
    @Autowired
    private AdminUserServiceImpl adminUserService;
    @Autowired
    private UserRoleServiceImpl userRoleService;
    @Autowired
    private CacheManager cacheManager;
    @Resource
    private CustomerBalanceServiceImpl customerBalanceService;
    @Resource
    private PaymentRecordsServiceImpl paymentRecordsService;

    @Transactional(rollbackFor = Exception.class)
    public CustomerVO createOrUpdateCustomer(CustomerSaveVO custVO){
        //客户名不重复、客户代码不重复
        if(custVO.getId() == null){
            return self.createCustomer(custVO);
        }
        boolean updStatus = self.updateCustomer(custVO);
        if(!updStatus){
            log.warn("update customer id =[{}] failed",custVO.getId());
            return null;
        }

        return  BeanUtils.toBean(custVO,CustomerVO.class);
    }

    @LogRecord(type = SYSTEM_USER_TYPE, subType = SYSTEM_CUSTOMER_UPDATE_SUB_TYPE, bizNo = "{{#customer.id}}",
            success = SYSTEM_CUSTOMER_UPDATE_STATUS_SUCCESS)
    @CacheEvict(value = RedisKeyConstants.CUSTOMER, key="#id")
    public Boolean updateCustomerStatus(Long id, Integer customerStatus){
        CustomerDO custCacheById = this.getById(id);
        if(custCacheById ==null){
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.CUSTOMER_NOT_EXISTS);
        }
        if(!customerStatus.equals(0)&& !customerStatus.equals(1)){
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.CONFIG_NOT_EXISTS);
        }
        custCacheById.setCustomerStatus(customerStatus);
        boolean updateStatus = this.updateById(custCacheById);
        cacheEvictByCode(custCacheById.getCustomerCode());
        LogRecordContext.putVariable("customer", custCacheById);

        adminUserService.updateUserStatusByLoginName(custCacheById.getCustomerCode(),customerStatus);
        log.info("更新客户状态信息");
        return updateStatus;
    }

    public List<CustomerDO> listCustomers(CustomerReqVO customerReqVO){
        LambdaQueryWrapperX<CustomerDO> custWrapperX = new LambdaQueryWrapperX<>();
        if(customerReqVO !=null){
            custWrapperX.likeIfPresent(CustomerDO::getCustomerCode,customerReqVO.getLikeCode());
            if(StrUtil.isNotBlank(customerReqVO.getLikeName())){
                custWrapperX.and(wrapper -> wrapper
                        .like(CustomerDO::getCustomerName, customerReqVO.getLikeName())
                        .or()
                        .like(CustomerDO::getCustomerShortName, customerReqVO.getLikeName()));
            }
            custWrapperX.eqIfPresent(CustomerDO::getCustomerStatus,customerReqVO.getStatus());
            if(customerReqVO.getBeginCreateTime() != null){
                custWrapperX.gtIfPresent(CustomerDO::getCreateTime,customerReqVO.getBeginCreateTime());
                custWrapperX.ltIfPresent(CustomerDO::getCreateTime,customerReqVO.getEndCreateTime());
            }
        }
        List<CustomerDO> list = this.list(custWrapperX);
        return list;
    }

    @CacheEvict(value = RedisKeyConstants.CUSTOMER, key="#id")
    public Boolean deleteCustById(Long id){
        CustomerDO custCacheById = this.getById(id);
        if(custCacheById == null){
            return true;
        }
        //1、检查用户是否有关联业务正在使用

        //2。没有关联业务使用时才能删除(逻辑删除)
        cacheEvictByCode(custCacheById.getCustomerCode());
        return this.removeById(id);
    }

    public Boolean resetCustomerPassword(Long customerId){
        CustomerDO custCacheById = self.getCustCacheById(customerId);
        if(custCacheById ==null){
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.CUSTOMER_NOT_EXISTS);
        }
        AdminUserDO userByUsername = adminUserService.getUserByUsername(custCacheById.getCustomerCode());
        if(userByUsername ==null){
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.USER_NOT_EXISTS);
        }
        String defaultPassword = adminUserService.getDefaultPassword();

        adminUserService.updateUserPassword(userByUsername.getId(),defaultPassword);
        return true;
    }



    public PageResult<CustomerDO> pageCustomers(CustomerReqVO customerReqVO){
        LambdaQueryWrapperX<CustomerDO> custWrapperX = new LambdaQueryWrapperX<>();
        if(customerReqVO ==null){
            return new PageResult<>();
        }
        custWrapperX.likeIfPresent(CustomerDO::getCustomerCode, customerReqVO.getLikeCode());
        if (StrUtil.isNotBlank(customerReqVO.getLikeName())) {
            custWrapperX.and(wrapper -> wrapper
                    .like(CustomerDO::getCustomerName, customerReqVO.getLikeName())
                    .or()
                    .like(CustomerDO::getCustomerShortName, customerReqVO.getLikeName()));
        }
        custWrapperX.eqIfPresent(CustomerDO::getCustomerStatus, customerReqVO.getStatus());
        if (customerReqVO.getBeginCreateTime() != null) {
            custWrapperX.gtIfPresent(CustomerDO::getCreateTime, customerReqVO.getBeginCreateTime());
            custWrapperX.ltIfPresent(CustomerDO::getCreateTime, customerReqVO.getEndCreateTime());
        }

        Page<CustomerDO> page=new Page<>(customerReqVO.getCurrent(),customerReqVO.getPageSize());
        Page<CustomerDO> pageRes = this.page(page, custWrapperX);
        return new PageResult<>(pageRes.getRecords(), pageRes.getTotal(),customerReqVO.getCurrent(),customerReqVO.getPageSize());
    }

    public PageResult<CustomerListVO> pageCustomersAndCost(CustomerReqVO customerReqVO){
        PageResult<CustomerDO> customerDOPage = self.pageCustomers(customerReqVO);
        PageResult<CustomerListVO> custVoPage = BeanUtils.toBean(customerDOPage, CustomerListVO.class);

        List<CustomerListVO> data = custVoPage.getData();
        if(CollUtil.isEmpty(data)){
            return BeanUtils.toBean(customerDOPage, CustomerListVO.class);
        }
        Map<String, CustomerListVO> customerDOMap = data.stream()
                .collect(Collectors.toMap(CustomerListVO::getCustomerCode, cust -> cust));

        List<CustomerBalanceDO> customerBalanceDOS = customerBalanceService.listCustBalances(customerDOMap.keySet());
        if(!CollUtil.isEmpty(customerBalanceDOS)){
            for (CustomerBalanceDO customerBalanceDO : customerBalanceDOS) {
                CustomerListVO custVo = customerDOMap.get(customerBalanceDO.getCustomerCode());
                custVo.setCustomerBalance(customerBalanceDO.getCbValue());
            }
        }
        List<PaymentSummaryDto> paymentSummaryDtos = paymentRecordsService.listCustomerTotalPay(customerDOMap.keySet());
        if(!CollUtil.isEmpty(paymentSummaryDtos)){
            for (PaymentSummaryDto paymentSummaryDto : paymentSummaryDtos) {
                CustomerListVO custVo = customerDOMap.get(paymentSummaryDto.getPayerCode());
                custVo.setCustomerRechargeTotal(paymentSummaryDto.getTotalAmount());
            }
        }
        return custVoPage;
    }

    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = RedisKeyConstants.CUSTOMER, key="#custVO.id")
    @LogRecord(type = SYSTEM_USER_TYPE, subType = SYSTEM_CUSTOMER_UPDATE_SUB_TYPE, bizNo = "{{#customer.id}}",
            success = SYSTEM_CUSTOMER_UPDATE_SUCCESS)
    public Boolean updateCustomer(CustomerSaveVO custVO){

        CustomerDO custById = this.getById(custVO.getId());
        if(custById ==null){
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.CUSTOMER_NOT_EXISTS);
        }
        validateUpdateCustomer(custVO,custById);
        BeanUtils.copyProperties(custVO,custById);
//        BeanUtils.mergeNonNullFields(custVO,custById,"id");
        boolean updateStatus = this.saveOrUpdate(custById);
//        boolean updateStatus = this.updateById(custById);
        cacheEvictByCode(custById.getCustomerCode());
        // 3. 记录操作日志上下文
        LogRecordContext.putVariable("customer", custById);
        log.info("更新客户信息");
        return  updateStatus;
    }

    public void validateUpdateCustomer(CustomerSaveVO customerVO,CustomerDO sourCust){
        if(!sourCust.getCustomerCode().equals(customerVO.getCustomerCode())){
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.CUSTOMER_CODE_CHANGED_ERROR);
        }
        if(StrUtil.isBlank(customerVO.getCustomerName())){
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.CUSTOMER_INFO_EMPTY,"name");
        }
        CustomerDO cust = this.getOne(new LambdaQueryWrapperX<CustomerDO>().eqIfPresent(CustomerDO::getCustomerName, customerVO.getCustomerName()).neIfPresent(CustomerDO::getId, sourCust.getId()));
        if(cust !=null){
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.INFO_REPEAT_ERROR,"name");
        }
//        //经营单位编码不重复
//        if(StrUtil.isNotBlank(customerVO.getCustomerTradeCode())){
//            validateTradeCodeUnique(customerVO.getCustomerTradeCode());
//        }
    }

    public CustomerListVO getAccountCustomer(){
        String customerCode = SecurityFrameworkUtils.getLoginUser().getUsername();
        if(StrUtil.isBlank(customerCode)){
            return null;
        }
        CustomerDO custCacheByCode = self.getCustCacheByCode(customerCode);
        CustomerListVO bean = BeanUtils.toBean(custCacheByCode, CustomerListVO.class);
        CustomerBalanceDO custBalanceByCustCode = customerBalanceService.getCustBalanceByCustCode(customerCode);
        bean.setCustomerBalance(custBalanceByCustCode.getCbValue());

       return bean;
    }

    @Cacheable(value = RedisKeyConstants.CUSTOMER, key = "#id", unless = "#result == null")
    public CustomerDO getCustCacheById(Long id){
        CustomerDO byId = this.getById(id);
        if(byId ==null){
            return null;
        }
        return byId;
    }

    public CustomerDO getCustCacheByUserId(Long  userId){
        AdminUserVO userByIdFromCache = adminUserService.getUserByIdFromCache(userId);
        CustomerDO byId = self.getCustCacheByCode(userByIdFromCache.getUsername());
        if(byId ==null){
            return null;
        }
        return byId;
    }


    @Cacheable(value = RedisKeyConstants.CUSTOMER_CODE, key = "#custCode", unless = "#result == null")
    public CustomerDO getCustCacheByCode(String custCode){
        CustomerDO byCode = this.getOne(new LambdaQueryWrapperX<CustomerDO>()
                .eq(CustomerDO::getCustomerCode,custCode));
        if(byCode ==null){
            return null;
        }
        return byCode;
    }


    @Transactional(rollbackFor = Exception.class)
    @LogRecord(type = SYSTEM_USER_TYPE, subType = SYSTEM_CUSTOMER_CREATE_SUB_TYPE, bizNo = "{{#customer.id}}",
            success = SYSTEM_CUSTOMER_CREATE_SUCCESS)
    public CustomerVO createCustomer(CustomerSaveVO custVO){
        //客户名不重复、客户代码不重复
        CustomerSaveVO customerVO = preSaveCustVerification(custVO);
        CustomerDO custDO = creatNewCustomer(customerVO);
        //1、保存客户信息
        boolean saveStatus = this.save(custDO);
        if(!saveStatus){
            log.warn("save customer failed, customer code=[{}]",custDO.getCustomerCode());
            return null;
        }

        //2、创建客户账号信息
        UserSaveReqVO userSaveReqVO =new UserSaveReqVO();
        userSaveReqVO.setUsername(custDO.getCustomerCode());
        userSaveReqVO.setNickname(custDO.getCustomerShortName());
        userSaveReqVO.setRemark(custDO.getCustomerName());
        userSaveReqVO.setEmail(custDO.getCustomerEmail());
        userSaveReqVO.setMobile(custDO.getCustomerTelephone());
        Long userId = adminUserService.createUser(userSaveReqVO);
        //3、进行客户授权，角色授权
        userRoleService.setCustomerRole(userId);

        //4、创建客户余额信息
        customerBalanceService.createCustomerBalance(custDO.getId(),custDO.getCustomerCode());
        // 3. 记录操作日志上下文
        LogRecordContext.putVariable("customer", custDO);
        return  BeanUtils.toBean(custDO,CustomerVO.class);
    }

    private CustomerDO creatNewCustomer(CustomerSaveVO customerVO){
        CustomerDO custDO = BeanUtils.toBean(customerVO, CustomerDO.class);
//        custDO.setCustomerBillDate()
        custDO.setAppToken(IdUtil.fastSimpleUUID());
        custDO.setAppKey(IdUtil.fastSimpleUUID());
        custDO.setCustomerAuditTime(new Date());
        return custDO;
    }
    /**
     * 1、客户数据校验
     * 2、客户默认数据补全
     * @param custVO
     * @return
     */
    public CustomerSaveVO preSaveCustVerification(CustomerSaveVO custVO){
        //检查数据重复
        validateCustomer(custVO);
        //补全默认数据
        custVO.setCustomerStatus(0);
        if(custVO.getCustomerType() == null){
            custVO.setCustomerType(1);
        }
        custVO.setCustomerMinBalance(new BigDecimal(0));
        if(StrUtil.isBlank(custVO.getCustomerCurrency())){
            custVO.setCustomerCurrency(CurrencyEnum.RMB.getValue());
        }
        custVO.setCustomerRatio(1.0f);
        if(custVO.getCustomerCashType() == null){
            custVO.setCustomerCashType(1);
        }
        return custVO;
    }
    
    
    public CustomerDO selectByCustomerCode(String customerCode){
        return this.baseMapper.selectOne(new LambdaQueryWrapperX<CustomerDO>().eqIfPresent(CustomerDO::getCustomerCode, customerCode));
    }   

    public void validateCustomer(CustomerSaveVO customerVO){
        if(StrUtil.isBlank(customerVO.getCustomerCode())){
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.CUSTOMER_INFO_EMPTY,"code");
        }
        if(StrUtil.isBlank(customerVO.getCustomerName())){
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.CUSTOMER_INFO_EMPTY,"name");
        }
        validateCustomerCodeUnique(customerVO.getCustomerCode());
        validateCustomerNameUnique(customerVO.getCustomerName());
        //经营单位编码不重复
        if(StrUtil.isNotBlank(customerVO.getCustomerTradeCode())){
            validateTradeCodeUnique(customerVO.getCustomerTradeCode());
        }
    }

    /**
     * 检查是否唯一，如果不唯一抛出异常
     */
    public void validateCustomerCodeUnique(String custCode){
        if(custCode.equals(RoleCodeEnum.ADMIN.getCode())){
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.CUSTOMER_CODE_REPEAT);
        }
        CustomerDO customerDO = this.baseMapper.selectOne(new LambdaQueryWrapperX<CustomerDO>().eqIfPresent(CustomerDO::getCustomerCode, custCode));
        if (customerDO !=null ){
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.CUSTOMER_CODE_REPEAT);
        }
    }

    /**
     * 检查名称唯一
     * @param customerName
     */
    public void validateCustomerNameUnique(String customerName){
        CustomerDO customerDO = this.baseMapper.selectOne(new LambdaQueryWrapperX<CustomerDO>().eqIfPresent(CustomerDO::getCustomerName, customerName));
        if (customerDO !=null){
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.CUSTOMER_NAME_REPEAT);
        }
    }


    /**
     * 检查经营单位编码是否唯一
     * @param tradeCode
     */
    @TenantIgnore
    public void validateTradeCodeUnique(String tradeCode){
        CustomerDO customerDO = this.baseMapper.selectOne(new LambdaQueryWrapperX<CustomerDO>().eqIfPresent(CustomerDO::getCustomerTradeCode, tradeCode));
        if (customerDO !=null){
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.CUSTOMER_TRADECODE_REPEAT);
        }
    }


    /**
     * 失效code 类型客户缓存
     * @param code
     */
    private void cacheEvictByCode(String code){
        if(StrUtil.isBlank(code)){
            return;
        }
        cacheManager.getCache(RedisKeyConstants.CUSTOMER_CODE).evict(code);
    }
}
